﻿using System;
using System.Linq;
using System.Reflection;

namespace SydEnergiPrototype.Logic
{
    public abstract class Parser
    {
        public Y ParseFromDto<T, Y>(T obj)
        {
            var returnObject = (Y)ManualFactory.CreateFromType(typeof(Y));
            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

            var tTypeInfos = typeof(T).GetProperties(flags);
            var yTypeInfos = typeof(Y).GetProperties(flags);

            foreach (var propertyInfo in tTypeInfos)
            {
                if (IsPrimitive(propertyInfo))
                {
                    HandlePrimitive(obj, yTypeInfos, returnObject, propertyInfo);
                }
                else
                {
                    DeserializeNonPrimitive(propertyInfo, obj, returnObject);
                }
            }
            return returnObject;
        }

        public Y ParseToDto<T, Y>(T obj)
        {
            var returnObject = Activator.CreateInstance<Y>();
            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

            var tTypeInfos = typeof(T).GetProperties(flags);
            var yTypeInfos = typeof(Y).GetProperties(flags);

            foreach (var propertyInfo in tTypeInfos)
            {
                if (IsPrimitive(propertyInfo))
                {
                    HandlePrimitive(obj, yTypeInfos, returnObject, propertyInfo);
                }
                else
                {
                    SerializeNonPrimitive(propertyInfo, obj, returnObject);
                }
            }

            return returnObject;
        }

        private static void HandlePrimitive<T, Y>(T obj, PropertyInfo[] yTypeInfos, Y returnObject, PropertyInfo propertyInfo)
        {
            object o = propertyInfo.GetValue(obj, null);

            if (propertyInfo.PropertyType == typeof (string) && o == null)
                o = "";

            foreach (var info in yTypeInfos.Where(i => i.Name == propertyInfo.Name))
            {
                info.SetValue(returnObject, o, null);
            }
        }

        protected abstract bool IsPrimitive(PropertyInfo property);
        protected abstract void SerializeNonPrimitive(PropertyInfo property, object source, object returnObject);
        protected abstract void DeserializeNonPrimitive(PropertyInfo property, object source, object returnObject);
    }
}
